View Javadoc
1 /* 2 * Title: S/MIME Project 3 * Description: S/MIME email sending capabilities 4 * @Author Vladan Obradovic 5 * @Version 2.0.1 6 */ 7 8 package org.webdocwf.util.smime.smime; 9 10 11 import org.webdocwf.util.smime.activation.CMSEnvelopedDataSource; 12 import org.webdocwf.util.smime.activation.CMSSignedDataSource; 13 import org.webdocwf.util.smime.exception.SMIMEException; 14 import org.webdocwf.util.smime.exception.ErrorStorage; 15 import org.webdocwf.util.smime.util.MimeAssist; 16 import org.webdocwf.util.smime.mail.MultipartGenerator; 17 import org.webdocwf.util.smime.util.ConvertAssist; 18 import org.webdocwf.util.smime.activation.StreamDataSource; 19 import javax.mail.internet.HeadersUtil; 20 import javax.mail.Session; 21 import javax.mail.Message; 22 import javax.mail.Multipart; 23 import javax.mail.Transport; 24 import javax.mail.MessagingException; 25 import javax.mail.internet.MimeMessage; 26 import javax.mail.internet.MimeBodyPart; 27 import javax.mail.internet.MimeMultipart; 28 import javax.mail.internet.InternetAddress; 29 import javax.activation.DataHandler; 30 import javax.activation.FileDataSource; 31 import java.util.Vector; 32 import java.util.Properties; 33 import java.util.SimpleTimeZone; 34 import java.util.GregorianCalendar; 35 import java.io.File; 36 import java.io.FileInputStream; 37 import java.io.InputStream; 38 import java.io.ByteArrayInputStream; 39 import java.security.Security; 40 import java.security.PrivateKey; 41 import java.security.KeyStore; 42 import java.security.cert.X509Certificate; 43 import java.security.cert.CertificateFactory; 44 import java.security.cert.CertificateException; 45 import sun.security.provider.Sun; 46 import org.bouncycastle.jce.provider.BouncyCastleProvider; 47 48 49 /*** 50 * SignedAndEnvelopedSMIME class is used for creating and sending signed and 51 * encrypted, or encrypted and signed S/MIME messages. Which process will be 52 * first (encrypting or signing) is defined in the method signingAndEnveloping 53 * by using appropriate parameter: SIGN_FIRST or ENCRYPT_FIRST.<BR> 54 * <BR> 55 * Email message is in general composed of the content of the message and of one or 56 * more attachments. The content is visible part of the message, and attacments are 57 * mostly files or other binary data, which are not visible parts of message and 58 * which are used by email as a transport medium. In this implementation content 59 * can be represented in two different forms: <BR> 60 * <BR> 61 * <UL><LI> 62 * text/plain (only text withouth any formating) or 63 * </LI> <LI> 64 * text/html (html coded view of message) 65 * </LI></UL> 66 * Also, content can be absent, but than at least one attachment must be added. 67 * Content can be set on few manners. For text/plain type it can be done in time 68 * of construction with constructor designed special for creation of text/plain 69 * messages. Also, text content can be created by any of setContent() methods, 70 * if construction of object was done by other constructor which create object 71 * with empty content. Construction with other constructor offers a few different 72 * posibilities for importing content data (File, InputStream, String) by using 73 * appropriate setContent() method. If method with four parameters is used, 3rd 74 * ant 4th parameters are not in use for text/plain message and could be set 75 * null. For setting text/html content, construction of object should be done 76 * only by second mentioned constructor, which creates object with empty content. 77 * Content should be populated by html code with setContent() method. 3rd 78 * parameter is used for resolving relative addresses of resources in html 79 * code (images, movies...) and 4th parameter serves as data source for resources 80 * that are on special way addressed in html code. Also, there is a setContent() 81 * method which doesn't care about resources and which creates message content 82 * withouth them. For more information refer to setContent() methods.<BR> 83 * <BR> 84 * Message can contain any number of attachments. Also, message can 85 * be wihouth any attachment, but then content must be present. Every attachment 86 * should be added by performing single addAttachment() method. Attachments 87 * can be added from file or from InputStream. Mime-type which corresponds to 88 * particular attachment is obtained according to extension of file name 89 * (virtual or real file name) passed to addAttachment() method. File mime.types 90 * in META_INF directory contains list of mime-types and corresponding extensions 91 * which are used in determination of mime-type. File can be changed to satisfy 92 * secific requrements. For more information refer to addAttachmenttent() 93 * method.<BR> 94 * <BR> 95 * Encryption of message is performed by symmetric encryption with random 96 * generated symmetric key. This key is then encrypted by assymetric encryption 97 * with a recipient's public key, and sent together with encrypted message to 98 * recipient in CMS (Cryptographic Message Syntax) enveloped object. For all 99 * recipients of message (if there is more than one) operation of encrypting 100 * symmetric key must be performed with his corresponding public key (from .cer 101 * file). Encryption can be performed by following algorithms and corresponding 102 * key sizes:<BR> 103 * RC2_CBC, 40 bits (default encryption)<BR> 104 * RC2_CBC, 64 bits<BR> 105 * RC2_CBC, 128 bits<BR> 106 * DES, 56 bits<BR> 107 * DES_EDE3_CBC, 128 bits<BR> 108 * DES_EDE3_CBC, 192 bits<BR> 109 * <BR> 110 * As a asymmetric algorithm, RSA algorithm is used.<BR> 111 * <BR> 112 * Message is implicitly signed in the case of both processing (signing 113 * and enveloping). External signing allows email receiving clients withouth 114 * implemented SMIME capabilities to preview the signed SMIME email messages. 115 * This possibilities have no importance in the case of both signing and enveloping 116 * (or enveloping and signing) because, before or after signing, the message is 117 * encrypted, so it is not readable.<BR> 118 * <BR> 119 * Message can be signed with or without Signed Attributes. Signed Attributes 120 * are one optional part of CMS (Cryptographic Message Syntax) signed objects, 121 * and consist of some atributes used in the process of signing (date and time 122 * of signing, capabilities of sending email client, message digest value...). 123 * If those attributes are ommited, only pure message is taken in the process 124 * of signing.<BR> 125 * <BR> 126 * Digest algorithm can be SHA1, MD2 or MD5 which depends on selected signing algorithm.<BR> 127 * <BR> 128 * Capabilities Attributes are one of Signed Attributes, and in the process of 129 * signing (if Signed Attributes are involved) can be set. This attributes 130 * indicate to recipient email client which encipher, symmetric and/or signature 131 * algorithms signer's email client preferes, end they can be used in the next 132 * communication between each others. Setting this posibilities is optional, but 133 * if it is set, order of adding gives the information about most preferes algorithms 134 * within paricular group of algorithms. Defined Capabilities Attributes in this version 135 * of Signed SMIME can be from group: RC2 40, RC2 64, RC2 128, DES and DES_EDE3 for 136 * symmetric encryption algorihms, from group: MD2 with RSA, MD5 with RSA, SHA1 with RSA 137 * and SHA1 with DSA for signing algorithms, and RSA for encipher algorithm. For more 138 * information see setCapabilities method in this class.<BR> 139 * <BR> 140 * Certificates of signers and their root authorities can be included in the 141 * signed message. This posibilities allow the recipient of signed SMIME 142 * message to automatically include signer's certificates as trusted, and verify 143 * signed message. This posibilities are optional.<BR> 144 * <BR> 145 * More than one signer can perform signing of message and they can use 146 * different signing algorithms. Digital signing can be performed by SHA1_WITH_RSA, 147 * MD2_WITH_RSA, MD5_WITH_RSA or SHA1_WITH_DSA.<BR> 148 * <BR> 149 */ 150 public class SignedAndEnvelopedSMIME { 151 152 /*** 153 * Container for MIME message 154 */ 155 private MimeMessage message; 156 157 /*** 158 * Storage for .cer files corresponding to appropriate enveloping session 159 */ 160 private Vector certArray = new Vector(0, 1); 161 162 /*** 163 * Storage for .pfx files corresponding to appropriate signing session (used 164 * for first type of addSigner function). 165 */ 166 private Vector ksArray = new Vector(0, 1); 167 168 /*** 169 * Storage for digest algorithm corresponding to appropriate signing session 170 * (used for first type of addSigner function). 171 */ 172 private Vector digestArray = new Vector(0, 1); 173 174 /*** 175 * Storage for byte[2] grouped indicators (used for first type of addSigner 176 * function). 177 */ 178 private Vector including = new Vector(0, 1); 179 180 /*** 181 * Storage for certificate chain corresponding to appropriate signing session 182 * (used for second type of addSigner function) 183 */ 184 private Vector certChainArray = new Vector(0, 1); 185 186 /*** 187 * Storage for private key corresponding to appropriate signing session (used 188 * for second type of addSigner function) 189 */ 190 private Vector privKeyArray = new Vector(0, 1); 191 192 /*** 193 * Storage for digest algorithm corresponding to appropriate signing session 194 * (used for second type of addSigner function) 195 */ 196 private Vector digestArray2 = new Vector(0, 1); 197 198 /*** 199 * Storage for byte[2] grouped indicators (used for second type of addSigner 200 * function) 201 */ 202 private Vector including2 = new Vector(0, 1); 203 204 /*** 205 * Storage for MIME bodyparts 206 */ 207 private Vector bodyPartArray = new Vector(0, 1); 208 209 /*** 210 * Storage for additional certificates 211 */ 212 private Vector aditionalCerts = new Vector(0, 1); 213 214 /*** 215 * Temporary storage for capabilities (after method addSigner, this object is 216 * copied to capabilities or capabilities2). 217 */ 218 private Vector capabilitiesTemp = new Vector(0, 1); 219 220 /*** 221 * Storage for capabilities (used for first type of addSigner function) 222 */ 223 private Vector capabilities = new Vector(0, 1); 224 225 /*** 226 * Storage for capabilities (used for second type of addSigner function) 227 */ 228 private Vector capabilities2 = new Vector(0, 1); 229 230 /*** 231 * Indication that at least one recipient must be TO (others may be CC or BCC) 232 */ 233 private boolean indicatorTo = false; 234 235 /*** 236 * Indicator of presence of plain text content 237 */ 238 private boolean textContentPresence = false; 239 240 /*** 241 * Initializes the JavaMail session for SMTP and MimeMessage for signing. 242 * Dynamically loads the BC and SUN provider necessary for encryption. This 243 * constructor is used for creating message with text/plain content. For creating 244 * html formated content (text/html), other constructor should be used in 245 * combination with one of setContent methods. Note that after using this 246 * constructor setContent method can be used only if "content" argument of 247 * constructor was given as null, otherwise setContent method can't be used 248 * because content is already set as text/plain. 249 * @param smtpHost name of SMTP host used for sending email 250 * @param fromAddress email address of sender (FROM field in email header) 251 * @param subject subject of email (SUBJECT field in email header) 252 * @param content text/plain content of email message 253 * @exception SMIMEException if smtpHost or fromAddress parameters are null. 254 * Also, it can be caused by non SMIMEException which is MessagingException. 255 */ 256 public SignedAndEnvelopedSMIME(String smtpHost, String fromAddress, String subject, 257 String content) throws SMIMEException { 258 try { 259 Security.addProvider(new BouncyCastleProvider()); // Dynamic loading the BC provider necessary for encryption 260 Security.addProvider(new Sun()); // Dynamic loading the SUN provider 261 262 if (smtpHost == null | fromAddress == null) 263 throw new SMIMEException(this, 1041); 264 Properties sesProp = new Properties(); 265 266 sesProp.setProperty("mail.smtp.host", smtpHost); 267 Session ses = Session.getInstance(sesProp); 268 269 message = new MimeMessage(ses); 270 InternetAddress from = new InternetAddress(fromAddress); 271 272 message.setFrom(from); 273 if (subject != null) 274 message.setSubject(subject); 275 if (content != null) { 276 MimeBodyPart mbp = new MimeBodyPart(); 277 278 mbp.setText(content); 279 bodyPartArray.addElement(mbp); 280 textContentPresence = true; 281 } 282 } catch (Exception e) { 283 throw SMIMEException.getInstance(this, e, "constructor"); 284 } 285 } 286 287 /*** 288 * Initializes the JavaMail session for SMTP and MimeMessage for signing. 289 * Dynamically loads the BC and SUN provider necessary for encryption. This 290 * constructor does not create content of message and it can be set later with 291 * one of setContent methods. Also, message can be left withouth content, but 292 * then at least one attachement must be added. 293 * @param smtpHost name of SMTP host used for sending email 294 * @param fromAddress email address of sender (FROM field in email header) 295 * @param subject subject of email (SUBJECT field in email header) 296 * @exception SMIMEException if smtpHost or fromAddress parameters are null. 297 * Also, it can be caused by non SMIMEException which is MessagingException. 298 */ 299 public SignedAndEnvelopedSMIME(String smtpHost, String fromAddress, String subject) 300 throws SMIMEException { 301 this(smtpHost, fromAddress, subject, null); 302 } 303 304 /*** 305 * Sets message content. Message content can be given in two differrent forms: 306 * text and html code. If content is type of text, parameter "type" should be 307 * "text/plain" and other two parameters are not in use (should be set null). 308 * If content is type of html code, parameter "type" should be set as "text/html", 309 * otherwise (if it is set as "text/plain") html code is processed as a plain 310 * text. This method can be performed only once.<BR> 311 * <BR> 312 * In case of html content, it is essential to (on appropriate way) associate some 313 * attributes of particular elements in html code ("background" or "src" atributes) 314 * with corresponding ressources (URL-s, relative file addresses or byte array 315 * streams). This resources should all be sent with message to enable recipient 316 * to see complete html message. Location of resources can be given in few 317 * different forms and depending on that, allocation resolving can be successful or 318 * not. Following text represents different possibilities for defining locations 319 * of resources (pictures, animations, sound...) inside of html code passed to 320 * this method, and necessery passed additional parameters used for resolving 321 * this resource locations.<BR> 322 * <BR> 323 * <UL> 324 * <LI>URL defined as: http://... is left unchanged. This resource is not sent 325 * with the message and it couldn't be seen by recipient if it is not online on 326 * the internet.</LI> 327 * <LI>URL defined as: file://... is transformed to corresponding Content ID if 328 * the resource can be found on specified location and it is sent with message.</LI> 329 * <LI>Absolute path, for example defined as: "c:\tmp\test\picture.bmp", is 330 * transformed to corresponding Content ID if the resource can be found on 331 * specified location, and is sent with message. If all resources in html 332 * code are specified with its absolute path, the 3rd parameter in this method 333 * can be null.</LI> 334 * <LI>Relative path of all resources specified in html code, for example 335 * defined as: ".\test\picture.bmp" and ".\example\flush.swf", must be defined 336 * to be relative to same directory path (in this case it is c:\tmp). This parameter 337 * (common directory path) is given as 3rd parameter in this method, and is named 338 * "path". If html code is obtained from .html file, necessery common directory 339 * path is usually path to this .html file. Location of resource is transformed 340 * to corresponding Content ID if the resource can be found on specified location. 341 * This location is sent with the message.</LI> 342 * <LI>Byte array stream as resource for html attribute must be referenced from 343 * html code as: <BR> 344 * <BR><PRE> 345 * *****nnn<virtual_file_name><BR> 346 * <BR></PRE> 347 * Five '*' characters (must be five) define that it is resource expected from 348 * the stream. Other three characters must be digits (000-999) and represent 349 * index of corresponding stream in stream array. virtual_file_name is name and 350 * extension assigned to data passed from stream. Name is used in construction of 351 * "name" parameter in Content-Type, while extension of file name is used in 352 * detection of appropriate mime-type. Lenght of virtual_file_name is not 353 * important. If there is no data referenced from byte array stream ,4th 354 * parameter of this method named "resources" can be null. Also, if all resources 355 * are passed through the array of streams, 3th parameter ("path") can be null. 356 * Location of resource is transformed to corresponding Content ID if no error 357 * has occured during the process of allocation.</LI> 358 * </UL> 359 * <BR> 360 * All mentioned resource allocation types can be combined together in the same 361 * html code, and all will be processed with appropriate use of this method.<BR> 362 * <BR> 363 * Note that number of resource references that are defined in html code by 364 * using virtual_file_names must be greater than or equal to number of elements 365 * in array of InputStream (4th parameter). If one resource (one element in array 366 * of IputStream) is used in html code more than once, it is advisable to use 367 * same virtual_file_name in html code because message is then sent with only 368 * one attached resource (image, movie...). It is essetntial that desired resource 369 * in input stream array corresponds to specified "nnn" part of virtual_file_name.<BR> 370 * <BR> 371 * If resources specified on any described name can not be found or resolved, 372 * or if any exception has occured during its processing, they won't be added and 373 * html message will be sent withouth them. 374 * @param content String representation of message content (text or html code). 375 * @param type type of given content. It can take values: "text/plain" or 376 * "text/html". 377 * @param path common directory path for relative file locations in html code. 378 * It can be null if all resources set absolute path or are defined by 379 * byte array streams, or if sending resources with relative address it is not desired. 380 * Also, it is set to null in case of text/plain message. 381 * @param resources way for representing resources used in the given html code 382 * which will be added to message as array of InputStream. Detail use 383 * of this argument is described above. It can be null if no resources as byte 384 * array stream are used, or if sending resources given in that way is not desired. 385 * Also, it is set to null in case of text/plain message. 386 * @exception SMIMEException if content is tried to be added twice, or in case of 387 * wrong "type" parameter. Also, it can be caused by non SMIMEException which can 388 * be one of the following: MessagingException UnsoportedEncodingException. 389 */ 390 public void setContent(String content, String type, String path, 391 InputStream[] resources) throws SMIMEException { 392 393 if (content != null) { 394 ByteArrayInputStream bais = null; 395 396 try { 397 bais = new ByteArrayInputStream(content.getBytes("ISO-8859-1")); 398 } catch (Exception e) { 399 throw SMIMEException.getInstance(this, e, "setContent"); 400 } 401 this.setContent(bais, type, path, resources); 402 403 } else 404 throw new SMIMEException(this, 1035); 405 } 406 407 /*** 408 * Sets message content from InputStream. This method can be performed only once. 409 * Message content can be given in two differrent forms: text and html code. If 410 * content is type of text, parameter "type" should be "text/plain", while if 411 * content is type of html code, parameter "type" should be set as "html/code". 412 * For further information refer to setContent method with four arguments 413 * (String, String, String, InputStream[] ) which is called by this method. 414 * @param content message content data given from any InputStream. 415 * Data can be text or html code and will be interpreted according to second 416 * parameter: "type". 417 * @param type type of given content. It can take values: "text/plain" or 418 * "text/html". 419 * @param path common directory path for relative file locations in html code. 420 * It can be null if all resources in html code have set absolute path or are 421 * defined by byte array streams, or if sending resources with relative address 422 * is not desired. Also, it is set to null in case of text/plain message. 423 * @param resources way for representing resources used in the given html code 424 * which will be added to message as array of InputStreams. Detail use 425 * of this argument is described in other setContent methods mentioned before. 426 * It can be null if no resources as byte array stream are used, or if sending 427 * resources given in that way is not desired. Also, it is set to null in case 428 * of text/plain message. 429 * @exception SMIMEException if content is tried to be added twice , in case of 430 * wrong "type" parameter or in case when parameter content is null. Also, it can 431 * be caused by non SMIMEException which is MessagingException. 432 */ 433 public void setContent(InputStream content, String type, String path, 434 InputStream[] resources) throws SMIMEException { 435 if (textContentPresence) 436 throw new SMIMEException(this, 1049); 437 if (content != null) { 438 try { 439 if (type.equalsIgnoreCase("text/plain")) { 440 MimeBodyPart mbp = new MimeBodyPart(); 441 String temp = new String(ConvertAssist.inStreamToByteArray(content), "ISO-8859-1"); 442 443 mbp.setText(temp, "ISO-8859-1"); 444 bodyPartArray.add(0, mbp); 445 textContentPresence = true; 446 } else if (type.equalsIgnoreCase("text/html")) { 447 MimeMultipart htmlMultipart = 448 MultipartGenerator.getHtmlMultipart(content, path, resources); 449 450 bodyPartArray.add(0, htmlMultipart); 451 textContentPresence = true; 452 } else 453 throw new SMIMEException(this, 1048); 454 } catch (Exception e) { 455 throw SMIMEException.getInstance(this, e, "setContent"); 456 } 457 } else 458 throw new SMIMEException(this, 1035); 459 } 460 461 /*** 462 * Sets message content from InputStream. This method can be performed only once. 463 * Message content can be given in two differrent forms: text and html code. If 464 * content is type of text, parameter "type" should be "text/plain", while if 465 * content is type of html code, parameter "type" should be set as "html/code". 466 * If html code content is set by this method, message will be generated withouth 467 * inclusion of the resources defined in paricular html element's attribute ("src" and 468 * "background"). Only plain html code will be sent and any reference to local 469 * file system resources will be useless for recipient of the message. HTTP referenced 470 * resources can still be available if recipient is online on Internet. Message 471 * generated on this way is smaller so encrypting process should be faster. 472 * @param content message content data given from any InputStream. 473 * Data could be text or html code and will be interpreted according to second 474 * parameter: "type". 475 * @param type type of given content. It can take values: "text/plain" or 476 * "text/html". 477 * @exception SMIMEException if content is tried to be added twice , in case of 478 * wrong "type" parameter or in case when parameter content is null. Also, it can 479 * be caused by non SMIMEException which is MessagingException. 480 */ 481 public void setContent(InputStream content, String type) throws SMIMEException { 482 if (textContentPresence) 483 throw new SMIMEException(this, 1049); 484 if (content != null) { 485 try { 486 if (type.equalsIgnoreCase("text/plain")) { 487 MimeBodyPart mbp = new MimeBodyPart(); 488 String temp = new String(ConvertAssist.inStreamToByteArray(content), "ISO-8859-1"); 489 490 mbp.setText(temp, "ISO-8859-1"); 491 bodyPartArray.add(0, mbp); 492 textContentPresence = true; 493 } else if (type.equalsIgnoreCase("text/html")) { 494 MimeMultipart htmlMultipart = 495 MultipartGenerator.getHtmlMultipart(content); 496 497 bodyPartArray.add(0, htmlMultipart); 498 textContentPresence = true; 499 } else 500 throw new SMIMEException(this, 1048); 501 } catch (Exception e) { 502 throw SMIMEException.getInstance(this, e, "setContent"); 503 } 504 } else 505 throw new SMIMEException(this, 1035); 506 } 507 508 /*** 509 * Sets message content from String. This method can be performed only once. 510 * Message content can be given in two differrent forms: text and html code. If 511 * content is type of text, parameter "type" should be "text/plain", while if 512 * content is type of html code, parameter "type" should be set as "html/code". 513 * If html code content is set by this method, message will be generated withouth 514 * inclusion of the resources defined in paricular html element's attribute ("src" or 515 * "background"). Only plain html code will be sent and any reference to local 516 * file system resources will be useless for recipient of the message. HTTP referenced 517 * resources can still be available if recipient is online on Internet. Message 518 * generated on this way is smaller, so encrypting process should be faster. 519 * @param content message content data given as String which can 520 * be text or html code and will be interpreted according to second parameter: 521 * "type". 522 * @param type type of given content. It can take values: "text/plain" or 523 * "text/html". 524 * @exception SMIMEException if content is tried to be added twice, or in case of 525 * wrong "type" parameter. Also, it can be caused by non SMIMEException which can 526 * be one of the following: MessagingException UnsoportedEncodingException. 527 */ 528 public void setContent(String content, String type) throws SMIMEException { 529 530 if (content != null) { 531 ByteArrayInputStream bais = null; 532 533 try { 534 bais = new ByteArrayInputStream(content.getBytes("ISO-8859-1")); 535 } catch (Exception e) { 536 throw SMIMEException.getInstance(this, e, "setContent"); 537 } 538 this.setContent(bais, type); 539 540 } else 541 throw new SMIMEException(this, 1035); 542 } 543 544 /*** 545 * Sets message content from file represented by File object. This method can be 546 * performed only once. Message content can be given in two differrent forms: 547 * text and html code. If content is type of text, parameter "type" should be 548 * "text/plain", while if content is type of html code, parameter "type" should 549 * be set as "html/code". For further information refer to setContent method 550 * with four arguments (String, String, String, InputStream[] ) which is called 551 * by this method. 552 * @param inFile location of file which is used for content of the message 553 * @param type type of given content. It can take values: "text/plain" or 554 * "text/html". 555 * @exception SMIMEException if content is tried to be added twice, in case of 556 * wrong "type" parameter, or if passed file (as File object) does not exist in 557 * file sistem. Also, it can be caused by non SMIMEException which can be one of 558 * the following: MessagingException or IOException. 559 */ 560 public void setContent(File inFile, String type) throws SMIMEException { 561 562 if (textContentPresence) 563 throw new SMIMEException(this, 1049); 564 if (inFile != null && inFile.exists()) { 565 try { 566 File inFileAbs = inFile.getAbsoluteFile().getCanonicalFile(); 567 String content = ConvertAssist.readFileToString(inFileAbs); 568 569 this.setContent(content, type, inFile.getParent(), null); 570 } catch (Exception e) { 571 throw SMIMEException.getInstance(this, e, "setContent"); 572 } 573 } else 574 throw new SMIMEException(this, 1034); 575 } 576 577 /*** 578 * Sets REPLY TO field in message header 579 * @param replyAddress email address used to reply 580 * @exception SMIMEException caused by non SMIMEException which is 581 * MessagingException. Also, javax.mail.internet.AddressException is thrown 582 * from instances of InternetAddress class (but AddressException extends 583 * MessagingException). 584 */ 585 public void setReply(String replyAddress) throws SMIMEException { 586 try { 587 InternetAddress reply[] = new InternetAddress[1]; 588 589 reply[0] = new InternetAddress(replyAddress); 590 message.setReplyTo(reply); 591 } catch (Exception e) { 592 throw SMIMEException.getInstance(this, e, "addRecipient"); 593 } 594 } 595 596 /*** 597 * Adds recipient address, type and .cer file of email recipient to signed and 598 * enveloped S/MIME message. 599 * @param recipientAddress email address of recipent (fields TO or CC or BCC 600 * in email message header) 601 * @param type should be TO, CC or BCC 602 * @param cerFileName path and file name with certificate corresponding 603 * to recipient (file with .cer extension) 604 * @exception SMIMEException if type of addressing of messages is not TO, CC 605 * or BCC. 606 * @exception SMIMEException caused by non SMIMEException which is 607 * MessagingException. 608 */ 609 public void addRecipient(String recipientAddress, String type, String cerFileName) 610 throws SMIMEException { 611 try { 612 if (!type.equalsIgnoreCase("TO") & !type.equalsIgnoreCase("BCC") & !type.equalsIgnoreCase("CC")) 613 throw new SMIMEException(this, 1042); 614 if (type.equalsIgnoreCase("TO")) { 615 message.addRecipients(Message.RecipientType.TO, recipientAddress); 616 indicatorTo = true; 617 } else if (type.equalsIgnoreCase("CC")) 618 message.addRecipients(Message.RecipientType.CC, recipientAddress); 619 else if (type.equalsIgnoreCase("BCC")) 620 message.addRecipients(Message.RecipientType.BCC, recipientAddress); 621 } catch (Exception e) { 622 throw SMIMEException.getInstance(this, e, "addRecipient"); 623 } 624 certArray.addElement(cerFileName); // adding location of .cer file in stack 625 } 626 627 /*** 628 * Adds file as attachment to email message 629 * @param fileName path and file name used for attachment 630 * @exception SMIMEException if passed file (as File object) does not exist in 631 * file sistem. Also, it can be caused by non SMIMEException which is 632 * MessagingException 633 */ 634 public void addAttachment(String fileName) throws SMIMEException { 635 File fn = new File(fileName); 636 637 this.addAttachment(fn); 638 } 639 640 /*** 641 * Adds file as attachment to email message 642 * @param file used for attachment represented as File object 643 * @exception SMIMEException if passed file (as File object) does not exist in 644 * file sistem. Also, it can be caused by non SMIMEException which is 645 * MessagingException 646 */ 647 public void addAttachment(File file) throws SMIMEException { 648 if (!file.exists()) 649 throw new SMIMEException(this, 1034); 650 MimeBodyPart attachment = new MimeBodyPart(); 651 FileDataSource fd = new FileDataSource(file); 652 653 try { 654 attachment.setDataHandler(new DataHandler(fd)); 655 attachment.setDisposition(attachment.ATTACHMENT); 656 attachment.setFileName(file.getName()); 657 } catch (Exception e) { 658 throw SMIMEException.getInstance(this, e, "addAttachment"); 659 } 660 661 bodyPartArray.addElement(attachment); 662 } 663 664 /*** 665 * Adds data from InputStream as attachment to email message 666 * @param data byte array from InputStream 667 * @param fileName virtual or real file name (wihouth path). Correct information 668 * about name; extension of file name is especially important. Name 669 * is used in construction of "name" parameter in Content-Type header line of 670 * body parts of mime message. Extension of file is used in detection of 671 * appropriate mime-type. 672 * @exception SMIMEException caused by non SMIMEException which is 673 * MessagingException 674 */ 675 public void addAttachment(InputStream data, String fileName) throws SMIMEException { 676 MimeBodyPart attachment = new MimeBodyPart(); 677 678 try { 679 attachment.setDataHandler(new DataHandler(new StreamDataSource(data, fileName))); 680 attachment.setDisposition(attachment.ATTACHMENT); 681 attachment.setFileName(fileName); 682 bodyPartArray.addElement(attachment); 683 } catch (Exception e) { 684 throw SMIMEException.getInstance(this, e, "addAttachment"); 685 } 686 } 687 688 /*** 689 * Sets Capabilities Attributes (method is optional, but if exists, must be 690 * performed before addSigner method). Depending on parameter type0, other five 691 * parameters make order in specific group of algorithms. Groups of algorithms 692 * with positions of specific algorithms are:<BR> 693 * (SIGNATURE, MD2 with RSA, MD5 with RSA, SHA1 with RSA, SHA1 with DSA, Unused field)<BR> 694 * (SYMMETRIC, RC2 40 bits, RC2 64 bits, RC2 128 bits, DES, DES_EDE3)<BR> 695 * (ENCIPHER, RSA, Unused field, Unused field, Unused field, Unused field)<BR> 696 * <BR> 697 * For example, if we wish to set Capabilities Attributes for symmetric algorithms 698 * in order: RC2 64 bits, RC2 40 bits and DES, encipher algorithm RSA (only possible 699 * in this version), and signature algorithms in order: SHA1 with RSA, MD5 with RSA 700 * and MD2 with RSA, we should make following lines of code<BR> 701 * <BR> 702 * setCapabilities ("SYMMETRIC", 2, 1, 0, 3, 0)<BR> 703 * setCapabilities ("ENCIPHER", 1, 0, 0, 0, 0)<BR> 704 * setCapabilities ("SIGNATURE", 3, 2, 1, 0, 0)<BR> 705 * <BR> 706 * 0 means exclusion of algorithm from the specified position in the method. It is 707 * free to decide which algorithm will be included, or which group of algorithm 708 * will be included in Capabilities Attributes. If no groups are added, capabilities 709 * attributes won't be added to Signed Attributes. If two or more signers will 710 * sign the message, and their capabilities are different, this method should 711 * be performed before every signing if we wish to specify Capabilities 712 * Attributes for all particular signers. If type0 parameter is set as:<BR> 713 * setCapabilities ("DEFAULT", 0, 0, 0, 0, 0)<BR> 714 * it is equivalent to:<BR> 715 * setCapabilities ("SYMMETRIC", 1, 0, 0, 0, 0)<BR> 716 * setCapabilities ("ENCIPHER", 0, 0, 1, 0, 0)<BR> 717 * setCapabilities ("SIGNATURE", 1, 0, 0, 0, 0)<BR> 718 * @param type0 sets group of algorithms for capabilities attributes. It can be set 719 * with values: SIGNATURE, SYMMETRIC, ENCIPHER or DEFAULT. 720 * @param par10 sets order in group of parameters, or exclude some algorithms 721 * from capabilities atributes. Can take values 1, 2, 3, 4 or 5 and 0 for 722 * exclusion of the particular algorithm. 723 * @param par20 same as for par10 724 * @param par30 same as for par10 725 * @param par40 same as for par10 726 * @param par50 same as for par10 727 * @exception SMIMEException if method is performed more than three times for one signer, 728 * or in case of wrong values of parameters. 729 */ 730 public void setCapabilities(String type0, int par10, int par20, int par30, 731 int par40, int par50) throws SMIMEException { 732 int[] tempType = { par10, par20, par30, par40, par50 }; 733 734 capabilitiesTemp.addElement(type0); 735 capabilitiesTemp.addElement(tempType); 736 if (capabilitiesTemp.size() > 6) 737 throw new SMIMEException(this, 1045); 738 } 739 740 /*** 741 * Adds signer to signed and enveloped S/MIME message. 742 * @param pfxfileName path and file name with certificate and private key 743 * corresponding to the sender of the message (file with .p12 or .pfx extension) 744 * @param password used to access to .pfx or .p12 file 745 * @param signingAlg algorithm used for signing (can be SHA1_WITH_RSA, 746 * MD2_WITH_RSA, MD5_WITH_RSA or SHA1_WITH_DSA). 747 * @param includingCert including/not including certificates to signed 748 * message 749 * @param includingSignAttrib including/not including signed attributes 750 * to signed message. Must be set to true in case of implicit signing 751 * @exception SMIMEException caused by non SMIMEException which can be one of the 752 * following: FileNotFoundException, NoSuchProviderException, KeyStoreException 753 * CertificateException, NoSuchAlgorithmException or IOException. 754 */ 755 public void addSigner(String pfxfileName, String password, String signingAlg, 756 boolean includingCert, boolean includingSignAttrib) throws SMIMEException { 757 try { 758 char[] paswCh = password.toCharArray(); 759 FileInputStream inPFX = new FileInputStream(pfxfileName); 760 KeyStore ks = KeyStore.getInstance("PKCS12", "BC"); 761 762 ks.load(inPFX, paswCh); 763 inPFX.close(); 764 boolean[] incl = { includingCert, includingSignAttrib }; 765 766 ksArray.addElement(ks); 767 digestArray.addElement(signingAlg); 768 including.addElement(incl); 769 if (capabilitiesTemp.size() != 0) { 770 for (int i = 0; i != capabilitiesTemp.size(); i++) 771 capabilities.addElement(capabilitiesTemp.elementAt(i)); 772 } 773 for (int i = 0; i != (6 - capabilitiesTemp.size()); i++) 774 capabilities.addElement(null); 775 capabilitiesTemp = new Vector(0, 1); 776 } catch (Exception e) { 777 throw SMIMEException.getInstance(this, e, "addSigner"); 778 } 779 } 780 781 /*** 782 * Adds signer to signed and enveloped S/MIME message. 783 * @param chain certificate chain. First certificate in array must be 784 * owner's certificate, and last certificate has to be root certificate 785 * @param privKey private key corresponding to owner's certificate (DSA 786 * or RSA depend on type of signing) 787 * @param signingAlg algorithm used for signing (can be SHA1_WITH_RSA, 788 * MD2_WITH_RSA, MD5_WITH_RSA or SHA1_WITH_DSA). 789 * @param includingCert including/not including certificates to signed 790 * message 791 * @param includingSignAttrib including/not including signed attributes 792 * to signed message. Must be set to true in case of implicit signing 793 */ 794 public void addSigner(X509Certificate[] chain, PrivateKey privKey, 795 String signingAlg, boolean includingCert, boolean includingSignAttrib) { 796 boolean[] incl = { includingCert, includingSignAttrib }; 797 798 certChainArray.addElement(chain); 799 privKeyArray.addElement(privKey); 800 digestArray2.addElement(signingAlg); 801 including2.addElement(incl); 802 if (capabilitiesTemp.size() != 0) { 803 for (int i = 0; i != capabilitiesTemp.size(); i++) 804 capabilities2.addElement(capabilitiesTemp.elementAt(i)); 805 } 806 for (int i = 0; i != (6 - capabilitiesTemp.size()); i++) 807 capabilities2.addElement(null); 808 capabilitiesTemp = new Vector(0, 1); 809 } 810 811 /*** 812 * Adds additional certificate to signed message 813 * @param cert X509 certificate 814 */ 815 public void addCertificate(X509Certificate cert) { 816 aditionalCerts.addElement(cert); 817 } 818 819 /*** 820 * Signes and envelopes message with default algorithm RC2, 40 bits 821 * @param type defines which action will be performed first (signing or 822 * enveloping). Allowed parameters are: SIGN_FIRST (signing first and then 823 * enveloping), and ENCRYPT_FIRST (enveloping first and then signing). 824 * @exception SMIMEException if one of recipients is not declared as TO 825 * recipient, if there is no message for enveloping, or if parameter "type" 826 * for message protection order in not SIGN_FIRST or ENCRYPT_FIRST. Also, it 827 * can be caused by non SMIMEException which can be one of the following: 828 * CertificateException, IOException, MessagingException, or FileNotFoundException. 829 */ 830 public void signingAndEnveloping(String type) throws SMIMEException { 831 this.signingAndEnveloping("RC2_CBC", 40, type); // type can take values "SIGN_FIRST" or "ENCRYPT_FIRST" 832 } 833 834 /*** 835 * Signes and envelopes message with given algorithm name and key length 836 * @param algorithmName name of chosen algorithm used for encryption 837 * @param keyLength key size in bits 838 * @param type defines which action will be performed first (signing or 839 * enveloping). Allowed parameters are: SIGN_FIRST (signing first and than 840 * enveloping), and ENCRYPT_FIRST (enveloping first and than signing). 841 * @exception SMIMEException if one of recipients is not declared as TO 842 * recipient, if there is no message for enveloping or if parameter "type" 843 * for message protection order in not SIGN_FIRST or ENCRYPT_FIRST. Also, it 844 * can be caused by non SMIMEException which can be one of the following: 845 * CertificateException, IOException, MessagingException, or FileNotFoundException. 846 */ 847 public void signingAndEnveloping(String algorithmName, int keyLength, String type) throws SMIMEException { 848 try { 849 if ((!type.equalsIgnoreCase("SIGN_FIRST")) && (!type.equalsIgnoreCase("ENCRYPT_FIRST"))) 850 throw new SMIMEException(this, 1046); 851 852 if (indicatorTo != true) 853 throw new SMIMEException(this, 1043); 854 if (textContentPresence & bodyPartArray.size() == 1) { // message contains only content 855 if (bodyPartArray.elementAt(0) instanceof MimeBodyPart) { // text/plain message 856 MimeBodyPart contentBody = (MimeBodyPart) bodyPartArray.elementAt(0); 857 858 message.setContent((String) contentBody.getContent(), contentBody.getContentType()); 859 message.setDisposition(message.INLINE); 860 } else // text/html message 861 message.setContent((MimeMultipart) bodyPartArray.elementAt(0)); 862 } else if (bodyPartArray.size() != 0) { 863 Multipart mp = new MimeMultipart(); 864 865 for (int i = 0; i != bodyPartArray.size(); i++) { 866 if (bodyPartArray.elementAt(i) instanceof MimeMultipart) { 867 MimeBodyPart forMulti = new MimeBodyPart(); 868 869 forMulti.setContent((MimeMultipart) bodyPartArray.elementAt(i)); 870 mp.addBodyPart(forMulti); 871 } else 872 mp.addBodyPart((MimeBodyPart) bodyPartArray.elementAt(i)); 873 } 874 message.setContent(mp); 875 } else 876 throw new SMIMEException(this, 1044); 877 878 CMSSignedDataSource sigDataSource = null; 879 CMSEnvelopedDataSource envDataSource = null; 880 881 if (type.equalsIgnoreCase("SIGN_FIRST")) { 882 sigDataSource = new CMSSignedDataSource(message, false); 883 for (int i = 0; i < ksArray.size(); i++) { 884 boolean[] incl = (boolean[]) including.elementAt(i); 885 886 for (int j = 6 * i; j != (6 * (i + 1)) && capabilities.elementAt(j) != null; j = j + 2) { 887 int[] capabil = (int[]) capabilities.elementAt(j + 1); 888 889 sigDataSource.setCapabilities((String) capabilities.elementAt(j), capabil[0], capabil[1], capabil[2], capabil[3], capabil[4]); 890 } 891 sigDataSource.addSigner((KeyStore) ksArray.elementAt(i), incl[0], incl[1], (String) digestArray.elementAt(i)); 892 } 893 for (int i = 0; i < certChainArray.size(); i++) { 894 boolean[] incl2 = (boolean[]) including2.elementAt(i); 895 896 for (int j = 6 * i; j != (6 * (i + 1)) && capabilities2.elementAt(j) != null; j = j + 2) { 897 int[] capabil = (int[]) capabilities2.elementAt(j + 1); 898 899 sigDataSource.setCapabilities((String) capabilities2.elementAt(j), capabil[0], capabil[1], capabil[2], capabil[3], capabil[4]); 900 } 901 sigDataSource.addSigner((X509Certificate[]) certChainArray.elementAt(i), (PrivateKey) privKeyArray.elementAt(i), incl2[0], incl2[1], (String) digestArray2.elementAt(i)); 902 } 903 for (int i = 0; i < aditionalCerts.size(); i++) { 904 sigDataSource.addCertificate((X509Certificate) aditionalCerts.elementAt(i)); 905 } 906 907 message.setDataHandler(new DataHandler(sigDataSource)); 908 HeadersUtil.updateHeaders(message); 909 envDataSource = new CMSEnvelopedDataSource(MimeAssist.messageConvertor(message), 910 algorithmName, keyLength); 911 912 for (int i = 0; i != certArray.size(); i++) { 913 InputStream inStream = new FileInputStream((String) (certArray.elementAt(i))); 914 CertificateFactory cf = CertificateFactory.getInstance("X.509"); 915 X509Certificate cert = (X509Certificate) cf.generateCertificate(inStream); 916 917 inStream.close(); 918 envDataSource.addRecipient(cert); 919 } 920 message.setDataHandler(new DataHandler(envDataSource)); 921 message.setDescription("Signed and Enveloped SMIME message."); 922 } else { 923 envDataSource = new CMSEnvelopedDataSource(message, algorithmName, keyLength); 924 for (int i = 0; i != certArray.size(); i++) { 925 InputStream inStream = new FileInputStream((String) (certArray.elementAt(i))); 926 CertificateFactory cf = CertificateFactory.getInstance("X.509"); 927 X509Certificate cert = (X509Certificate) cf.generateCertificate(inStream); 928 929 inStream.close(); 930 envDataSource.addRecipient(cert); 931 } 932 933 message.setDataHandler(new DataHandler(envDataSource)); 934 HeadersUtil.updateHeaders(message); 935 sigDataSource = new CMSSignedDataSource(MimeAssist.messageConvertor(message), 936 false); 937 938 for (int i = 0; i < ksArray.size(); i++) { 939 boolean[] incl = (boolean[]) including.elementAt(i); 940 941 for (int j = 6 * i; j != (6 * (i + 1)) && capabilities.elementAt(j) != null; j = j + 2) { 942 int[] capabil = (int[]) capabilities.elementAt(j + 1); 943 944 sigDataSource.setCapabilities((String) capabilities.elementAt(j), capabil[0], capabil[1], capabil[2], capabil[3], capabil[4]); 945 } 946 sigDataSource.addSigner((KeyStore) ksArray.elementAt(i), incl[0], incl[1], (String) digestArray.elementAt(i)); 947 } 948 for (int i = 0; i < certChainArray.size(); i++) { 949 boolean[] incl2 = (boolean[]) including2.elementAt(i); 950 951 for (int j = 6 * i; j != (6 * (i + 1)) && capabilities2.elementAt(j) != null; j = j + 2) { 952 int[] capabil = (int[]) capabilities2.elementAt(j + 1); 953 954 sigDataSource.setCapabilities((String) capabilities2.elementAt(j), capabil[0], capabil[1], capabil[2], capabil[3], capabil[4]); 955 } 956 sigDataSource.addSigner((X509Certificate[]) certChainArray.elementAt(i), (PrivateKey) privKeyArray.elementAt(i), incl2[0], incl2[1], (String) digestArray2.elementAt(i)); 957 } 958 for (int i = 0; i < aditionalCerts.size(); i++) { 959 sigDataSource.addCertificate((X509Certificate) aditionalCerts.elementAt(i)); 960 } 961 message.setDataHandler(new DataHandler(sigDataSource)); 962 message.setDescription("Enveloped and Signed SMIME message."); 963 } 964 965 message.setDisposition(message.ATTACHMENT); 966 SimpleTimeZone tz = (SimpleTimeZone) SimpleTimeZone.getDefault(); 967 GregorianCalendar cal = new GregorianCalendar(tz); 968 969 message.setSentDate(cal.getTime()); 970 971 clean(); 972 } catch (Exception e) { 973 throw SMIMEException.getInstance(this, e, "signingAndEnveloping"); 974 } 975 } 976 977 /*** 978 * Returns SMIME Message. 979 * @return Signed and encrypted (or encrypted and signed) S/MIME message. 980 */ 981 public MimeMessage getSignedAndEnvelopedSMimeMessage() { 982 return message; 983 } 984 985 /*** 986 * Sends S/MIME message to SMTP host 987 * @exception MessagingException caused by use of methods from objects of class 988 * Transport. 989 */ 990 public void send() throws MessagingException { 991 Transport.send(message); 992 } 993 994 /*** 995 * Releases unnecessary memory 996 */ 997 private void clean() { 998 ksArray = null; 999 digestArray = null; 1000 including = null; 1001 certChainArray = null; 1002 privKeyArray = null; 1003 digestArray2 = null; 1004 including2 = null; 1005 bodyPartArray = null; 1006 aditionalCerts = null; 1007 certArray = null; 1008 capabilitiesTemp = null; 1009 capabilities = null; 1010 capabilities2 = null; 1011 System.gc(); // Calling garbage collector 1012 } 1013 } 1014

This page was automatically generated by Maven